package com.zcxx.cotton.pick.util;

import com.ruoyi.common.utils.bean.BeanUtils;
import com.ruoyi.common.utils.http.SendUtils;
import com.zcxx.cotton.pick.domain.*;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.stream.Collectors;

import org.locationtech.jts.geom.Coordinate;
import org.locationtech.jts.geom.GeometryFactory;
import org.locationtech.jts.geom.Point;
import org.locationtech.jts.geom.Polygon;

public class MapUtil {


    /**
     *
     * 筛选属于地块内的坐标点
     * @param firstbuList
     * @param elements
     * @return
     */
    public static List<PickMapInfo> filterMapInfo(
            List<PickMapInfoFirstbu> firstbuList,
            List<PickMapElement> elements) {
        List<PickMapInfo> result = new ArrayList<>();

        for (PickMapInfoFirstbu firstbu : firstbuList) {
            double firstbuLongitude = Double.parseDouble(firstbu.getLongitude());
            double firstbuLatitude = Double.parseDouble(firstbu.getLatitude());


            for (PickMapElement element : elements) {
                // type: 13=地块 7=地点 30=文件夹
                if (element.getType() == 13){
                    String[] coords = element.getDetail().split(",");

                    // 检查是否在多边形内
                    if (isPointInPolygon(firstbuLongitude, firstbuLatitude, coords)) {
                        PickMapInfo pickMapInfo = new PickMapInfo();

                        BeanUtils.copyProperties(firstbu, pickMapInfo);
                        result.add(pickMapInfo);
                        break;
                    }
                }
            }
        }

        return result;

    }

    /**
     * 判断一个点是否在多边形内部
     * 使用射线法算法实现：从目标点向右水平发射射线，计算与多边形边的交点数量
     * 如果交点数量为奇数，则点在多边形内部；如果为偶数，则点在多边形外部
     *
     * @param pointLng 点的经度坐标
     * @param pointLat 点的纬度坐标
     * @param polygonCoords 多边形顶点坐标数组
     * @return 如果点在多边形内部返回true，否则返回false
     */
    public static boolean isPointInPolygon(double pointLng, double pointLat, String[] polygonCoords) {
        // 处理空数组或坐标点不足的情况
        if (polygonCoords.length < 4) {
            return false;
        }

        // 检查是否需要处理闭环情况（第一个点和最后一个点相同）
        int coordLength = polygonCoords.length;
        boolean isClosed = false;

        // 判断是否闭环：第一个点和最后一个点相同
        if (coordLength >= 4) {
            double startLat = Double.parseDouble(polygonCoords[0]);
            double startLng = Double.parseDouble(polygonCoords[1]);
            double endLat = Double.parseDouble(polygonCoords[coordLength - 2]);
            double endLng = Double.parseDouble(polygonCoords[coordLength - 1]);
            isClosed = (Math.abs(startLat - endLat) < 1e-10) && (Math.abs(startLng - endLng) < 1e-10);
        }

        // 确定实际处理的坐标点数量
        int effectiveLength = isClosed ? coordLength - 2 : coordLength; // 如果闭环则排除最后一个点

        // 如果处理后的点数不足4个（即2个点），无法构成多边形
        if (effectiveLength < 4) {
            return false;
        }

        // 初始化前一个顶点索引
        int j = effectiveLength - 2;
        // 用于记录射线与多边形边的交点数量是否为奇数
        boolean oddNodes = false;

        // 遍历多边形的每条边
        for (int i = 0; i < effectiveLength; i += 2) {
            // 纬度在前，经度在后
            double polyLat1 = Double.parseDouble(polygonCoords[i]);       // 纬度1
            double polyLng1 = Double.parseDouble(polygonCoords[i + 1]);   // 经度1
            double polyLat2 = Double.parseDouble(polygonCoords[j]);       // 纬度2
            double polyLng2 = Double.parseDouble(polygonCoords[j + 1]);   // 经度2

            // 判断水平射线是否与当前边相交
            if ((polyLat1 < pointLat && polyLat2 >= pointLat) ||
                    (polyLat2 < pointLat && polyLat1 >= pointLat)) {
                // 计算射线与边的交点横坐标，如果交点在目标点左侧，则计数加1
                if (polyLng1 + (pointLat - polyLat1) / (polyLat2 - polyLat1) * (polyLng2 - polyLng1) < pointLng) {
                    oddNodes = !oddNodes;
                }
            }
            // 更新前一个顶点索引
            j = i;
        }

        // 返回交点数量是否为奇数的结果
        return oddNodes;
    }



    /////////////////////////////////////////////////////以下是筛选采摘信息的///////////////////////////////////////////////////////////////

    /**
     *
     * 筛选属于地块内的坐标点PickInfo
     * @param firstbuList
     * @param elements
     * @return
     */
    public static List<PickInfo> filterPickInfo(List<PickInfoFirstbu> firstbuList, List<PickMapElement> elements) {
        List<PickInfo> result = new ArrayList<>();
        for (PickInfoFirstbu firstbu : firstbuList) {
            double firstbuLongitude = Double.parseDouble(firstbu.getLongitude());
            double firstbuLatitude = Double.parseDouble(firstbu.getLatitude());
            for (PickMapElement element : elements) {
                // type: 13=地块 7=地点 30=文件夹
                if (element.getType() == 13){
                    String s = transformString(element.getDetail());//分组
                    String jiaohuan=swapLatLonPositions1(s);
                    //System.out.println(jiaohuan);
                    double[][] polygon = convertStringToDoubleArray(jiaohuan);
                    // 检查是否在多边形内
                    if (isPointInPolygon(firstbuLongitude, firstbuLatitude, polygon)) {
                        PickInfo pickInfo = new PickInfo();
                        BeanUtils.copyProperties(firstbu, pickInfo);
                        pickInfo.setId(element.getId());
                        result.add(pickInfo);
                        break;
                    }
                }
            }
        }
        return result;
    }



    /**
     * 2. 使用JTS Topology Suite（推荐）
     * @param longitude
     * @param latitude
     * @param polygonCoords
     * @return
     */
    public static boolean isPointInPolygon(double longitude, double latitude,
                                           double[][] polygonCoords) {
        GeometryFactory geometryFactory = new GeometryFactory();

        // 创建多边形
        Coordinate[] coordinates = new Coordinate[polygonCoords.length + 1];
        for (int i = 0; i < polygonCoords.length; i++) {
            coordinates[i] = new Coordinate(polygonCoords[i][0], polygonCoords[i][1]);
        }
        // 闭合多边形
        coordinates[polygonCoords.length] = coordinates[0];
        Polygon polygon = geometryFactory.createPolygon(coordinates);

        // 创建点
        Point point = geometryFactory.createPoint(new Coordinate(longitude, latitude));
        return polygon.contains(point);
    }


    /**
     * 值和值之间用逗号，每两个值放一个分号
     * @param input
     * @return
     */
    public static String transformString(String input) {
        String[] parts = input.split(",");
        StringBuilder result = new StringBuilder();

        for (int i = 0; i < parts.length; i++) {
            if (i > 0) {
                // 每两个元素之间保留一个逗号，其余用分号
                if (i % 2 == 1) {
                    result.append(",");
                } else {
                    result.append(";");
                }
            }
            result.append(parts[i].trim());
        }
        return result.toString();
    }


    /**
     * 交换纬度和经度的位置方法2
     * @param input
     * @return
     */
    public static String swapLatLonPositions1(String input) {
        return Arrays.stream(input.split(";"))
                .map(String::trim)
                .map(point -> {
                    String[] coords = point.split(",");
                    if (coords.length != 2) {
                        throw new IllegalArgumentException("每个点必须有且只有两个坐标值");
                    }
                    return coords[1].trim() + "," + coords[0].trim();
                })
                .collect(Collectors.joining("; "));
    }



    /**
     * 把字符串组成的字符串转成数组
     * @return
     */
    public static double[][] convertStringToDoubleArray(String str) {
        return Arrays.stream(str.split(";"))
                .map(s -> s.trim().split(","))
                .map(arr -> new double[]{
                        Double.parseDouble(arr[0]),
                        Double.parseDouble(arr[1])
                })
                .toArray(double[][]::new);
    }
}
